//============================================================================
// Copyright (C) 2009-2013 Brett R. Jones 
// All Rights Reserved
//
// You may redistribute and/or modify for non commercial and commercial uses 
// provided this copyright notice remains in place and is not modified
//
// This code is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// brettjones1900@gmail.com
// http://www.p2panarchy.com
//============================================================================
 
#include <string.h>
#include "RcSysSktMgrLib.h"
#include <VxUtilLib/VxGlobals.h>

//============================================================================
// our receive function to receive Socket states etc
void RcSysSktMgr::HandleSktCallback(  RcSktBase * poSkt )
{
	if( VxIsAppShuttingDown() )
	{
		return;
	}

	switch( poSkt->GetCallbackReason() )
	{
	case eSktCallbackReasonConnectError:
#ifdef DEBUG_SKTS
		log_msg( LOG_ERROR, "RcSysSktMgr::HandleSktCallback: Skt %d connect error %s\n", poSkt->m_iSktId, poSkt->DescribeSktError( poSkt->m_rcLastError ) );
#endif // DEBUG_SKTS
		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, NULL, eSktCallbackReasonConnectError );
		}
		else
		{
			AddSktRxEvent( new RcSysRxEvent(poSkt, NULL, eSktCallbackReasonConnectError, poSkt->m_rcLastError) );
		}
		break;
	case eSktCallbackReasonConnected:
#ifdef DEBUG_SKTS
		log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback: Skt %d connected from %s port %d\n", poSkt->m_iSktId, poSkt->GetRemoteIp(), poSkt->m_oRmtIp.getPort() );
#endif // DEBUG_SKTS
		if( m_bProcessEventsDirectly )
		{
			//log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback: processing Connected Event\n" );
			ProcessSktEvent( poSkt, NULL, eSktCallbackReasonConnected );
		}
		else
		{
			AddSktRxEvent( new RcSysRxEvent(poSkt, NULL, eSktCallbackReasonConnected) );
		}
		break;
	case eSktCallbackReasonData:
		if( 16 > poSkt->GetSktBufDataLen() )
		{
			// dont even bother.. not enough to decrypt
			break;
		}
		if( poSkt->IsUdpSocket() )
		{
			//log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback:Udp Data Skt %d\n", poSkt->m_iSktId );
			HandleUdpData( poSkt );
			break;
		}
		else
		{
			//log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback:Tcp Data Skt %d\n", poSkt->m_iSktId );
			HandleTcpData( poSkt );
			break;
		}
		break;
	case eSktCallbackReasonClosed:
		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, NULL, eSktCallbackReasonClosed );
		}
		else
		{
			AddSktRxEvent( new RcSysRxEvent(poSkt, NULL, eSktCallbackReasonClosed, poSkt->m_rcLastError) );
		}
#ifdef DEBUG_SKTS
		log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback: Skt %d closed %s\n", poSkt->m_iSktId, poSkt->DescribeSktError( poSkt->m_rcLastError ) );
#endif // DEBUG_SKTS
		break;
	case eSktCallbackReasonError:
#ifdef DEBUG_SKTS
		log_msg( LOG_ERROR, "RcSysSktMgr::HandleSktCallback: Skt %d error %s\n", poSkt->m_iSktId, poSkt->DescribeSktError( poSkt->m_rcLastError ) );
#endif // DEBUG_SKTS
		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, NULL, eSktCallbackReasonError );
		}
		break;

	case eSktCallbackReasonClosing:
#ifdef DEBUG_SKTS
		log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback: eSktCallbackReasonClosing Skt %d \n", poSkt->m_iSktId );
#endif // DEBUG_SKTS
		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, NULL, eSktCallbackReasonClosing );
		}
		break;
	case eSktCallbackReasonConnecting:
#ifdef DEBUG_SKTS
		log_msg( LOG_INFO, "RcSysSktMgr::HandleSktCallback: eSktCallbackReasonConnecting Skt %d \n", poSkt->m_iSktId );
#endif // DEBUG_SKTS
		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, NULL, eSktCallbackReasonConnecting );
		}
		break;
	default:
		log_msg( LOG_ERROR, "RcSysSktMgr::HandleSktCallback: Skt %d error %s\n", poSkt->m_iSktId, poSkt->DescribeSktError( poSkt->m_rcLastError ) );
	}
};

//============================================================================
////! handle udp data callbacks from sockets
void RcSysSktMgr::HandleUdpData( RcSktBase * poSkt )
{
	if( VxIsAppShuttingDown() )
	{
		return;
	}
	int	iDataLen =	poSkt->GetSktBufDataLen();
	char * pBuf = poSkt->GetSktReadBuf();
	if( false == VxIsEncryptable( iDataLen ) )
	{
		//log_msg( LOG_INFO, "RcSysSktMgr::HandleUdpData: Skt %d not encryptable len %d\n", poSkt->m_iSktId, iDataLen );
		poSkt->SktBufAmountRead( iDataLen );
		return;
	}

	// decrypt
	RcSysPktHdr * poPkt = (RcSysPktHdr *)pBuf;
	VxSymDecrypt( &poSkt->m_oRxKey, pBuf, iDataLen );
	if( poPkt->isValidSysPkt() )
	{
		//log_msg( LOG_INFO, "RcSysSktMgr::HandleUdpData: Skt %d valid packet\n", poSkt->m_iSktId );
		// valid pkt
		if( poPkt->IsPktAnnounce() )
		{
			//log_msg( LOG_INFO, "RcSysSktMgr::HandleUdpData: Skt %d from %s port %d PktAnnounce\n", poSkt->m_iSktId, poSkt->m_strRmtIp.c_str(), poSkt->m_u16RmtPort );
			if( m_bProcessEventsDirectly )
			{
				//log_msg( LOG_INFO, "RcSysSktMgr::HandleUdpData: ProcessingSktEvent\n" );
				ProcessSktEvent( poSkt, poPkt, eSktCallbackReasonData );
			}
			else
			{
				//log_msg( LOG_INFO, "RcSysSktMgr::HandleUdpData: AddSktRxEvent\n" );
				AddSktRxEvent( new RcSysRxEvent( poSkt, poPkt, eSktCallbackReasonData ) );
			}
		}
	}
	else
	{
		log_msg( LOG_ERROR, "RcSysSktMgr::HandleUdpData: Invalid Packet\n" );
	}
	poSkt->SktBufAmountRead( iDataLen );
}

//============================================================================
//! handle received tcp data
void RcSysSktMgr::HandleTcpData( RcSktBase * poSktIn )
{
	if( VxIsAppShuttingDown() )
	{
		return;
	}
	RcSktBase * poSkt = (RcSktBase *)poSktIn;
	RCODE			rc = 0;
	U32				u32UsedLen = 0;
	int	iDataLen =	poSkt->GetSktBufDataLen();
	// app should get the buffer ( this also locks it from being modified by threads )
	// then read the data then call Amount read
	RcSysPktHdr * poPktHdr = (RcSysPktHdr *)poSkt->GetSktReadBuf();
	char *	pSktBuf = (char *)poPktHdr;
	if( poSkt->IsFirstRxPacket() )
	{
		if( iDataLen < 16 )
		{
			HackerOffense( NULL, 1, poSkt->GetRemoteIpBinary(), "Hacker too short data attack from ip %s\n", poSkt->GetRemoteIp() );
			poSkt->CloseSkt( 619 );
		}
		if( false == poSkt->IsRxEncryptionKeySet() )
		{
			bool bIsAscii = true;
			for( int i = 0; i < 16; i++ )
			{
				// check if all ascii
				if( (pSktBuf[i] < 2) || (pSktBuf[i] > 127) )
				{
					bIsAscii = false;
					break;
				}
			}

			if( bIsAscii )
			{
				// handle first packet
				if( 0 == strncmp( pSktBuf, "http://PING", 11 ) )
				{
					if( 0 == strncmp( pSktBuf, "http://PING /", 13 ) )
					{
						// ping .. return pong
						poSkt->SendData( "http://PONG /", 14, true );
					}
					else
					{
						log_msg( LOG_ERROR, "Unrecognized PING\n");
					}
					return;
				}
				else
				{
					// check to see if is http://
					if( ( 0 == strncmp( pSktBuf, "http://", 7 ) )	||
						( 0 == strncmp( pSktBuf, "GET /", 5 ) )		||
						( 0 == strncmp( pSktBuf, "POST /", 6 ) ) )
					{

						if( ( false == bIsAscii ) || ( false == poSkt->IsAcceptSocket()) )
						{
							// somebody tried to send crap after receiving a packet announce
							HackerOffense( NULL, 1, poSkt->GetRemoteIpBinary(), "Hacker http attack from ip %s\n", poSkt->GetRemoteIp() );
							// release the mutex
							poSkt->SktBufAmountRead( 0 );
							poSkt->CloseSkt( 657 );
							return;
						}
						// http connection 
						// this is a special case
						// Web Page Plugin will take over the socket and connection
						poSkt->SktBufAmountRead( 0 );
						if( rc = HandleHttpConnection( poSkt ) )
						{
							poSkt->CloseSkt( 619 );
						}
						return;
					}
				}
			}
		}

		// not http.. better be a announce packet
		if( (false == poSkt->IsAcceptSocket()) &&
			(false == poSkt->IsRxEncryptionKeySet() ) )
		{
			// we connected out but never set our key
			vx_assert( false );
		}

		if( false == poSkt->IsRxEncryptionKeySet() )
		{
			// this data has not been decrypted.. set encryption key and
			// decrypt it
			m_oPktAnn.DebugDump();
			vx_assert( poSkt->m_u32RxDecryptedLen == 0 );
			GenerateRxConnectionKey( poSkt, &m_oPktAnn.m_oDirectConnectId );
			poSkt->DecryptRecieveData();
		}

		if( false == poPktHdr->IsPktAnnounce()) 
		{
			// somebody tried to send crap after receiving a packet announce
			log_msg( LOG_SKT, "First packet is not Announce pkt type %d length %d\n", poPktHdr->m_u16PktType,  poPktHdr->m_u16PktLen );

			HackerOffense( NULL, 1, poSkt->GetRemoteIpBinary(), "Hacker no announcement attack from ip %s\n", poSkt->GetRemoteIp() );
			// release the mutex
			poSkt->SktBufAmountRead( 0 );
			poSkt->CloseSkt( 658 );
			return;
		}

		if( false == poPktHdr->IsPktAllHere(iDataLen) )
		{
			// not all of packet arrived
			return;
		}

		// pkt announce has arrived
		if( false == ((RcSysPktAnnounce *)poPktHdr)->isValidSysPkt() )
		{
			// invalid announcement packet
			poSkt->SetIsFirstRxPacket( false ); 
			// release the mutex
			poSkt->SktBufAmountRead( 0 );
			log_msg( 0, "Invalid Packet announce from ip %s\n", poSkt->GetRemoteIp() );
			// disconnect
			poSkt->CloseSkt( 659 );
		}

		//BRJ TODO check if is in our ignore list

		// BRJ que for qui thread

		//rc = RcSysHandlePkt( poSkt, poPktHdr );
		//if( rc )
		//{
		//	poSkt->SktBufAmountRead( 0 );
		//	log_msg( 0, "Closing after Packet announce from ip %s with rc %d\n", poSkt->GetRemoteIp(), rc );
		//	// disconnect
		//	poSkt->CloseSkt( 660 );
		//	return;
		//}
		log_msg( 0, "Got Ann on Skt %d\n", poSkt->m_iSktId );

		u32UsedLen = poPktHdr->GetPktLength();
		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, poPktHdr, eSktCallbackReasonData );
		}
		else
		{
			AddSktRxEvent( new RcSysRxEvent(poSkt, poPktHdr, eSktCallbackReasonData) );
		}

		// success
		poSkt->SetIsFirstRxPacket( false );
		// fall thru in case there are more packets
	}
	while( TRUE )
	{
		//log_msg( 0, "AppRcpCallback.. 3 Skt %d num %d Total Len %d Used Len %d decrypted Len %d\n", 
		//	poSkt->GetSocketId(),
		//	poSkt->m_iSktId,
		//	u32Len,
		//	u32UsedLen,
		//	poSkt->m_u32DecryptedLen );
		if( false == poSkt->IsConnected() )
		{
			//socket has been closed
			log_msg( 0, "RcSysSktMgr::HandleRecieveData: callback was for data but socket is not connected\n" );
			return;
			break;
		}

		if( poSkt->m_u32RxDecryptedLen <= u32UsedLen )
		{
			//all decrypted data used up
			break;
		}

		if( 16 > ( poSkt->m_u32RxDecryptedLen - u32UsedLen ) )
		{
			//not enough for a valid packet
			break;
		}

		poPktHdr = (RcSysPktHdr *)&pSktBuf[ u32UsedLen ];
		if( false == poPktHdr->isValidRcPkt() )
		{
			// invalid data
			HackerOffense( 0, 2, poSkt->GetRemoteIpBinary(), "Invalid VxPktHdr\n" );
			// release the mutex
			poSkt->SktBufAmountRead( 0 );
			poSkt->CloseSkt( 622 );
			return;
		}
		//get type of packet
		U16 u16PktType = poPktHdr->getPktTypeAndPluginNum();
		U16 u16PktLen = poPktHdr->GetPktLength();
		if( RC_SYS_PKT_TYPE_DIE == u16PktType )
		{
			poSkt->CloseSkt(680);
			poSkt->SktBufAmountRead( 0 );
			return;
		}

		if( u32UsedLen + u16PktLen > poSkt->m_u32RxDecryptedLen )
		{
			//not all of packet is here
			//log_msg( 0,  "AppRcpCallback.. Skt %d num %d Not all of packet arrived\n", 
			//		poSkt->GetSocketId(),
			//		poSkt->m_iSktId );
			break;
		}

		if( m_bProcessEventsDirectly )
		{
			ProcessSktEvent( poSkt, poPktHdr, eSktCallbackReasonData );
		}
		else
		{
			AddSktRxEvent( new RcSysRxEvent(poSkt, poPktHdr, eSktCallbackReasonData) );
		}
		//we used up this packet
		u32UsedLen += u16PktLen;
		//log_msg( 0, "AppRcpCallback.. skt %d num %d Used Len %d of %d \n", 
		//			poSkt->GetSocketId(),
		//			poSkt->m_iSktId,
		//			u32UsedLen, 
		//			poSkt->m_u32DecryptedLen );

	}			

	poSkt->SktBufAmountRead( u32UsedLen );
}

//============================================================================
//! process events in tx que
void RcSysSktMgr::ProcessTxEventQue( void )
{
	m_oTxSemaphore.Wait();
	if( m_oTxEventThread.isAborted() )
	{
		return;
	}

	RcSysTxEvent * poEvent;
	m_oTxEventMutex.Lock();
	while( m_aoTxEvents.size() )
	{
		poEvent = RemoveSktTxEvent();
		m_oTxEventMutex.Unlock();

		if( m_oTxEventThread.isAborted() )
		{
			return;
		}

		if( poEvent )
		{
			if( eSktCallbackReasonData == poEvent->m_eCallbackReason )
			{
				if( poEvent->m_poSkt->IsConnected() )
				{
					if( poEvent->m_poSkt->IsTxEncryptionKeySet() )
					{
						poEvent->m_poSkt->TxEncrypted( (const char *)poEvent->m_poPkt, poEvent->m_poPkt->m_u16PktLen, poEvent->m_bDisconnectAfterSend );
					}
					else
					{
						poEvent->m_poSkt->SendData( (const char *)poEvent->m_poPkt, poEvent->m_poPkt->m_u16PktLen, poEvent->m_bDisconnectAfterSend );
					}
				}
			}
			delete poEvent;
		}
		else
		{
			m_oTxEventMutex.Lock();
			break;
		}

		// unlock then lock so other threads have chance
		m_oTxEventMutex.Lock();
	}

	m_oTxEventMutex.Unlock();
	//if( false == bHadEvents)
	//{
	//	// didnt process any events so do a bit of sleeping
	//	VxSleep( 200 );
	//}
}
